package cn.blnp.net.flowable.task.receive;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

/**
 * <h3>接收任务案例</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/5/12 16:18
 */
@Slf4j
@SpringBootTest
public class ReceiveTaskTest {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    /**
     *  <p>
     *      <b>接收任务和手动任务类似，不同之处在于手动任务会直接通过，而接收任务则会停下来等待触发，只有被触发才会继续流转。</b>
     *  </p>
     *  <p>
     *      接收任务通常用于表示由外部完成的但需要耗费一定时间的工作。当流程流转到接收任务时，流程状态会持久化到数据库中，这意味着该流程将一直处于等待状态，
     *  等待被触发。当完成工作后，需要触发流程离开接收任务才能继续流转，在Flowable中可以调用运行时服务的trigger()系列方法实现。
     *  <ol>
     *      <li>
     *          trigger(String executionId) //触发指定的执行流
     *      </li>
     *      <li>
     *          trigger(String executionId, Map<String, Object> processVariables)   //触发指定的执行流，同时传递流程变量
     *      </li>
     *      <li>
     *          trigger(String executionId, Map<String, Object> processVariables, Map<String, Object> transientVariables)   //触发指定的执行流，同时传递流程变量和瞬时变量
     *      </li>
     *  </ol>
     *  </p>
     *  <h2>案例演示：</h2>
     *  <hr/>
     *  <p>
     *      该流程为账号激活流程：当客户账号激活申请提交后，先流转到“管理员审核”用户任务，操作完成后流转到“等待激活结果”接收任务，该任务被触发后继续流转，直到流程结束。
     *      <b>流程定义文件：</b>{@link flow/receiveTaskProcess.bpmn20.xml}<br/>
     *      <b>流程预览图：</b><br/>
     *      <img src="https://cdn4.winhlb.com/2025/05/12/6821b12f4a2ed.png" />
     *  </p>
     **/
    @Test
    public void test() {
        Deployment deploy = repositoryService.createDeployment()
                .name("接收任务演示案例")
                .addClasspathResource("flow/receiveTaskProcess.bpmn20.xml")
                .deploy();
        log.info("部署流程定义：{}", deploy.getId());
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        log.info("流程定义编号：{}", procDef.getId());
        //启动流程实例
        ProcessInstance procInst = runtimeService.startProcessInstanceById(procDef.getId());
        TaskQuery taskQuery = taskService.createTaskQuery()
                .processInstanceId(procInst.getId());

        //查询并完成第一个任务
        Task firstTask = taskQuery.singleResult();
        log.info("即将完成第一个任务，当前任务名称：{}", firstTask.getName());
        taskService.complete(firstTask.getId());
        //查询并完成第二个任务
        Task secondTask = taskQuery.singleResult();
        log.info("即将完成第二个任务，当前任务名称：{}", secondTask.getName());
        taskService.complete(secondTask.getId());
        //查询执行到此接收任务的执行实例
        Execution execution = runtimeService.createExecutionQuery()
                .processInstanceId(procInst.getId()) //使用流程实例ID查询
                .activityId("receiveTask1") //当前活动的ID，对应ReceiveTask的节点ID
                .singleResult();
        //设置流程变量
        Map<String, Object> varMap = MapUtil.builder(new HashMap<String, Object>())
                .put("result", "账号成功激活！")
                .build();
        //触发流程离开接收任务继续往下执行
        runtimeService.trigger(execution.getId(), varMap);
        //查询历史流程实例
        HistoricProcessInstance hisProcInst = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(procInst.getId())
                .singleResult();
        if (hisProcInst.getEndTime() != null) {
            log.info("当前流程已结束，结束时间：{}", DateUtil.formatDateTime(hisProcInst.getEndTime()));
        }

        /**
         *  演示结果：
         * 2025-05-12 16:35:34.839  INFO 64812 --- [           main] c.b.n.f.task.receive.ReceiveTaskTest     : 部署流程定义：123bfe55-2f0c-11f0-8519-50ebf6ec8f6d
         * 2025-05-12 16:35:34.848  INFO 64812 --- [           main] c.b.n.f.task.receive.ReceiveTaskTest     : 流程定义编号：receiveTaskProcess:1:12ad0fa8-2f0c-11f0-8519-50ebf6ec8f6d
         * 2025-05-12 16:35:34.972  INFO 64812 --- [           main] c.b.n.f.task.receive.ReceiveTaskTest     : 即将完成第一个任务，当前任务名称：账号激活申请
         * 2025-05-12 16:35:35.040  INFO 64812 --- [           main] c.b.n.f.task.receive.ReceiveTaskTest     : 即将完成第二个任务，当前任务名称：管理员审核
         * 2025-05-12 16:35:35.146  INFO 64812 --- [           main] c.b.n.f.t.r.ReceiveTaskExecutionListener : 当前为接收任务，节点名称：等待激活结果，备注：这是接收任务，等待触发后流程离开继续往下执行
         * 2025-05-12 16:35:35.146  INFO 64812 --- [           main] c.b.n.f.t.r.ReceiveTaskExecutionListener : 接收任务已被触发，处理结果为：账号成功激活！
         * 2025-05-12 16:35:35.309  INFO 64812 --- [           main] c.b.n.f.task.receive.ReceiveTaskTest     : 当前流程已结束，结束时间：2025-05-12 16:35:35
         **/
    }
}
